/*
 * Created on Apr 14, 2004
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package abdn.graph.growl.editor;

//import FishEyeMenu;

import java.awt.Point;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.net.*;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;

import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
import javax.swing.JSplitPane;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

import abdn.graph.growl.GraphException;
import abdn.graph.growl.GraphUtil;
import abdn.graph.growl.KRPolicyOWL;
import abdn.graph.growl.MetadataPanel;
/*import abdn.graph.growl.ObjectPropertyPanel;
import abdn.graph.growl.RelationPropertyPanel;*/
import abdn.graph.growl.RenderGrowl;
import abdn.graph.growl.OWLtoGraph;
import abdn.graph.ontobrowser.ESDOntoBrowser;
import abdn.graph.ontobrowser.GUIPolicy;
import abdn.graph.ontobrowser.GUIPolicyEditor;
import abdn.graph.ontobrowser.KRPolicyWrite;
import abdn.graph.ontobrowser.OBEdge;
import abdn.graph.ontobrowser.OBNode;
import abdn.graph.ontobrowser.OBObjectNode;
//import abdn.graph.ontobrowser.OBRelationNode;
import abdn.graph.ontobrowser.ESDOntoBrowser.RestoreExactGraph;
import org.semanticweb.owl.model.OWLException;
import org.semanticweb.owl.model.OWLOntology;

import com.touchgraph.graphlayout.LocalityUtils;
import com.touchgraph.graphlayout.Node;
import com.touchgraph.graphlayout.NodeFilter;
import com.touchgraph.graphlayout.TGException;
import com.touchgraph.graphlayout.graphelements.TGForEachNode;
import abdn.Cleon.util.*;

/**
 * @author skrivov
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 * Code has been modified by Quentin Reul for CleanONTO.
 */
public class EditorGUI extends JPanel
    implements GUIPolicy, GUIPolicyEditor {

  public static final int OPTION_FAIL = -10;
  public static final int OPTION_EDGE = -20;
  public static final int OPTION_RNODE = -30;

  public static final int EDITING_OBJECT = 100;
  public static final int EDITING_RELATION = 200;
  public static final int EDITING_EDGE = 300;

  public static final int TOOL_BROWSE = 0;
  public static final int TOOL_DELETE = -1;
  public static final int TOOL_CLASS = 2;
  public static final int TOOL_DATATYPE = 3;
  public static final int TOOL_INDIVIDUAL = 4;
  public static final int TOOL_DATA_VALUE = 5;
  public static final int TOOL_EDGE_ISA = 6;
  public static final int TOOL_EDGE_EQVIVALENT = 7;
  public static final int TOOL_PROPERTY = 8;
  public static final int TOOL_PROPERTY_VALUE = 9;
  public static final int TOOL_DATA_PROPERTY = 10;
  public static final int TOOL_DATA_PROPERTY_VALUE = 11;
  public static final int TOOL_RESTRICTION_SOME = 12;
  public static final int TOOL_RESTRICTION_ALL = 13;
  public static final int TOOL_RESTRICTION_NUMBER = 14;
  public static final int TOOL_RESTRICTION_VALUE = 15;
  public static final int TOOL_DATA_RESTRICTION_SOME = 16;
  public static final int TOOL_DATA_RESTRICTION_ALL = 17;
  public static final int TOOL_DATA_RESTRICTION_NUMBER = 18;
  public static final int TOOL_DATA_RESTRICTION_VALUE = 19;
  public static final int TOOL_OR = 20;
  public static final int TOOL_AND = 21;
  public static final int TOOL_NOT = 22;
  public static final int TOOL_ONE_OF = 23;
  public static final int TOOL_EQVIVALENT = 24;
  public static final int TOOL_DISJOINT = 25;
  public static final int TOOL_INDIVIDUALS_EQUAL = 26;
  public static final int TOOL_INDIVIDUALS_DIFFERENT = 27;

  public static final int TOOL_INVERSE_OF = 28;

  public static int ALL = 0;
  public static int ABOX = 1;
  public static int TBOX = 2;
  public static int HIERARCH = 3;
  public static int HIERARCH_INST = 4;
  public static int RBOX = 5;

  public static int LIST_CLASSES = 0;
  public static int LIST_INSTANCES = 1;
  public static int LIST_RELATIONS = 2;

  protected boolean objPaneIsActive = true;
  public boolean suppressSelectedEvent = false;

  /*protected ObjectPropertyPanel objPanel = null;
  protected RelationPropertyPanel relPanel = null;*/

  public ESDOntoBrowser esdOntoBrowser = null;
  protected com.touchgraph.graphlayout.TGPanel tgPanel = null;
  protected javax.swing.JScrollBar hScrollBar = null;
  protected javax.swing.JScrollBar vScrollBar = null;
  protected javax.swing.JToolBar jToolBar = null;

  protected javax.swing.JButton showMoreButton = null;
  protected javax.swing.JButton showLessButton = null;
  protected javax.swing.JButton viewAllButton = null;
  protected javax.swing.JButton viewHierarchyButton = null;
  protected javax.swing.JButton toggleRBoxButton = null;

  private JPopupMenu nodePopup = null;
  private JPopupMenu backPopup = null;

  Node popupNode = null;

  public int editingItem = 100;
  public int editToolPressed = 0;
  public int createNodeType;
  public int geType;

  boolean doLayout = true;

  ImageIcon stopLayoutIco = null;
  ImageIcon doLayoutIco = null;
  ImageIcon editIco = null;
  ImageIcon browseIco = null;
  ImageIcon viewMetadataIco = null;
  ImageIcon viewGraphIco = null;

  public boolean editMode = false;

  private JPopupMenu nodeEditPopup = null;
  private JPopupMenu backEditPopup = null;

  private boolean mdataPanelIsActive = false;

  private boolean loading = false;
  private boolean inNotification;

  private MetadataPanel mdataPanel = null;

  private File currentFile = null;

  //  @jve:visual-info decl-index=0 visual-constraint="664,233"
  private int mode = 0;

  private int lastSplit = -1;

  private boolean controlsVisible = true;

  private OWLOntology ontology = null;

  //private WordNetData wordNet;
  private WordNetDictionary wordNet;

  //private Set includeOntologies = null;

  private JSplitPane splitPane = null;
  private ClassViewer selectPanel = null;

  //  @jve:visual-info decl-index=0 visual-constraint="229,143"
  private javax.swing.JButton zoomInButton = null;
  private javax.swing.JButton zoomOutButton = null;

  private javax.swing.JMenuBar mainMenuBar = null;

  //  @jve:visual-info decl-index=0 visual-constraint="362,346"
  private javax.swing.JMenu fileMenu = null;

  private javax.swing.JMenuItem loadMenuItem = null;

  private javax.swing.JFileChooser fileChooser = null;

  //  @jve:visual-info decl-index=0 visual-constraint="668,4"

  private javax.swing.JMenuItem saveOWLRDFMenuItem = null;

  private javax.swing.JMenuItem saveAbstractMenuItem = null;

  private javax.swing.JMenuItem exitMenuItem = null;

  private javax.swing.JPopupMenu obPopup = null;

  private IncrMatchComboBox classSelectCombo = null;

  private boolean rBoxIsActive = false;

  private javax.swing.JButton toggleEditButton = null;

  private javax.swing.JLabel jLabel = null;

  //private EditToolsPanel editToolboxl = null;

 // private EditToolsRBox editToolsRBox = null;

  private javax.swing.JButton jButton = null;

  private javax.swing.JButton layoutButton = null;

  private javax.swing.JMenuItem jMenuItem = null;

  private javax.swing.JButton fileNewButton = null;

  private javax.swing.JButton fileOpenButton = null;

  private javax.swing.JButton fileSaveButton = null;

  public void initialize() {
    stopLayoutIco =
     new javax.swing.ImageIcon(
      getClass().getResource("/abdn/graph/growl/images/stopLayout.png"));
    doLayoutIco =
     new javax.swing.ImageIcon(
      getClass().getResource("/abdn/graph/growl/images/doLayout.png"));
    editIco =
     new javax.swing.ImageIcon(
      getClass().getResource("/abdn/graph/growl/images/edit.png"));
    browseIco =
     new javax.swing.ImageIcon(
      getClass().getResource("/abdn/graph/growl/images/book.png"));
    viewMetadataIco =
     new javax.swing.ImageIcon(
      getClass().getResource("/abdn/graph/growl/images/OWL.png"));
    viewGraphIco =
     new javax.swing.ImageIcon(
      getClass().getResource("/abdn/graph/growl/images/viewGraph.png"));
    this.setLayout(new java.awt.BorderLayout());
    this.add(getJToolBar(), java.awt.BorderLayout.NORTH);
    this.add( getTgPanel(), java.awt.BorderLayout.CENTER);
    this.setSize(667, 500);
    this.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
    //this.setPreferredSize(new java.awt.Dimension(400, 700));
    addGrounding();
  }
  private void addGrounding() {
    try {
      (new OWLtoGraph(esdOntoBrowser,wordNet)).addGrounding();
      if (mdataPanel != null) {
        mdataPanel.setMetadata(esdOntoBrowser.krPolicy);
      }
      updateObjectList(LIST_CLASSES);
      updateObjectPropertyList();
      updateDataPropertyList();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
  /* (non-Javadoc)
   * @see abdn.graph.ontobrowser.GUIPolicy#setObjectNode(abdn.graph.ontobrowser.OBObjectNode)
   */
  public void setObjectNode(OBObjectNode on) {
    this.updateUI();
    /*if (!objPaneIsActive) {
      objPaneIsActive = true;
      this.updateUI();
    }*/
  }

  /**
   * This method initializes hScrollBar
   *
   * @return javax.swing.JScrollBar
   */
  protected javax.swing.JScrollBar getHScrollBar() {
    if (hScrollBar == null) {
      hScrollBar = new javax.swing.JScrollBar();
      hScrollBar.setOrientation(javax.swing.JScrollBar.HORIZONTAL);
    }
    return hScrollBar;
  }

  /**
   * This method initializes vScrollBar
   *
   * @return javax.swing.JScrollBar
   */
  protected javax.swing.JScrollBar getVScrollBar() {
    if (vScrollBar == null) {
      vScrollBar = new javax.swing.JScrollBar();
    }
    return vScrollBar;
  }

  /* (non-Javadoc)
       * @see abdn.graph.ontobrowser.GUIPolicy#setVerticalSB(javax.swing.JScrollBar)
   */
  public void setVerticalSB(JScrollBar sb) {
    vScrollBar = sb;

  }

  /* (non-Javadoc)
       * @see abdn.graph.ontobrowser.GUIPolicy#setHorizontalSB(javax.swing.JScrollBar)
   */
  public void setHorizontalSB(JScrollBar sb) {
    hScrollBar = sb;

  }

  /**
   * This method initializes zoomInButton
   *
   * @return javax.swing.JButton
   */
  protected javax.swing.JButton getZoomInButton() {
    if (zoomInButton == null) {
      zoomInButton = new javax.swing.JButton();
      zoomInButton.setText("");
      zoomInButton.setIcon(new javax.swing.ImageIcon(
          getClass().getResource("/abdn/graph/growl/images/zoom in-tran.png")));
      zoomInButton.setToolTipText("Zoom In");
      zoomInButton.setPreferredSize(new java.awt.Dimension(24, 24));
      zoomInButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent e) {
          esdOntoBrowser.zoomScroll.zoomIn();
        }
      });
    }

    return zoomInButton;
  }

  /**
   * This method initializes zoomOutButton
   *
   * @return javax.swing.JButton
   */
  protected javax.swing.JButton getZoomOutButton() {
    if (zoomOutButton == null) {
      zoomOutButton = new javax.swing.JButton();
      zoomOutButton.setText("");
      zoomOutButton.setIcon(new javax.swing.ImageIcon(
          getClass().getResource("/abdn/graph/growl/images/zoom ou-tran.png")));
      zoomOutButton.setToolTipText("Zoom Out");
      zoomOutButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent e) {
          esdOntoBrowser.zoomScroll.zoomOut();
        }
      });
    }
    return zoomOutButton;
  }

  /**
   * This method initializes showMoreButton
   *
   * @return javax.swing.JButton
   */
  protected javax.swing.JButton getShowMoreButton() {
    if (showMoreButton == null) {
      showMoreButton = new javax.swing.JButton();
      showMoreButton.setText("");
      showMoreButton.setToolTipText("Show More");
      showMoreButton.setIcon(new javax.swing.ImageIcon(
          getClass().getResource("/abdn/graph/growl/images/showmore.png")));
      showMoreButton.addActionListener(esdOntoBrowser.showMoreAction);
    }
    return showMoreButton;
  }

  /**
   * This method initializes showLessButton
   *
   * @return javax.swing.JButton
   */
  protected javax.swing.JButton getShowLessButton() {
    if (showLessButton == null) {
      showLessButton = new javax.swing.JButton();
      showLessButton.setText("");
      showLessButton.setToolTipText("Show Less");
      showLessButton.setIcon(new javax.swing.ImageIcon(
          getClass().getResource("/abdn/graph/growl/images/showless.png")));
      showLessButton.addActionListener(esdOntoBrowser.showLessAction);
    }
    return showLessButton;
  }

  public void viewClassHierarchy() {
    LocalityUtils localityUtils = tgPanel.getLocalityUtils();
    tgPanel.clearSelect();
    tgPanel.restoreAll();
    // GraphUtil.hideThingNode(false);
    TGForEachNode fen = new TGForEachNode() {
      public void forEachNode(Node node) {
        if (node instanceof OBObjectNode) {
          if ( ( (OBObjectNode) node).objectCategory != RenderGrowl.OBJ_CLASS) {
            node.markedForRemoval = true;
            node.isHiden = true;
          }
        }
        else {
          node.markedForRemoval = true;
          node.isHiden = true;
        }
      }
    };
    tgPanel.getCompleteEltSet().forAllNodes(fen);
    localityUtils.removeMarkedNodes();
    tgPanel.fireResetEvent();
    tgPanel.resetDamper();
  }

  /* (non-Javadoc)
   * @see abdn.graph.ontobrowser.GUIPolicy#setEditing(boolean)
   */
  public void setEditing(boolean editing) {
    // TODO Auto-generated method stub
  }

  public void viewClassHierarchyAndInstances() {
    LocalityUtils localityUtils = tgPanel.getLocalityUtils();
    tgPanel.clearSelect();
    tgPanel.restoreAll();
    // GraphUtil.hideThingNode(false);
    TGForEachNode fen = new TGForEachNode() {
      public void forEachNode(Node node) {
        if (node instanceof OBObjectNode) {
          if (! ( ( (OBObjectNode) node).objectCategory == RenderGrowl.OBJ_CLASS
          || ( (OBObjectNode) node).objectCategory == RenderGrowl.OBJ_INDIVIDUAL)) {
            node.markedForRemoval = true;
            node.isHiden = true;
          }
        } else {
          node.markedForRemoval = true;
          node.isHiden = true;
        }
      }
    };

    tgPanel.getCompleteEltSet().forAllNodes(fen);
    localityUtils.removeMarkedNodes();
    tgPanel.fireResetEvent();
    tgPanel.resetDamper();

  }

  private ActionListener expandNodeAction = new ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent e) {
      esdOntoBrowser.expand(popupNode, null);
    }
  };

  private ActionListener hideNodeAction = new ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent e) {
      Node sel = tgPanel.getSelect();

      popupNode.isHiden = true;
      popupNode.markedForRemoval = true;
      for (int i = 0; i < popupNode.edgeNum(); i++) {
        Node newNode = popupNode.edgeAt(i).getOtherEndpt(popupNode);
      }
      if (sel != null && sel.isHiden) {
        tgPanel.setSelect(null);
      }
      esdOntoBrowser.getTGPanel().getLocalityUtils().removeMarkedNodes();
      tgPanel.fireResetEvent();
      tgPanel.resetDamper();
    }
  };

  private ActionListener localNodeAction = new ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent e) {
      tgPanel.setSelect(popupNode);
      tgPanel.setLocale(popupNode, 2);
    }
  };
  /**
   * Function used as part of the GenericNodeClass
   * @param node being the OBObjectNode
   * @return all children for a node
   */
  static public Vector getSubVectClasses(OBObjectNode node ) {
    Vector children = new Vector();
    for ( int i = 0; i < node.edges.size(); i++ ) {
      OBEdge e = (OBEdge) node.edges.get(i);
      if ( e.to == node && e.edgeOrder == OBEdge.SUBCLASS_OF_EDGE && GraphUtil.isClass( (OBNode) e.from)) {
        children.add(e.from);
      }
    }
    return children;
  }

  static public Set getSubClasses(OBObjectNode node) {
    HashSet children = new HashSet();
    for (Iterator i = node.edges.iterator(); i.hasNext(); ) {
      OBEdge e = (OBEdge) i.next();
      if (e.to == node && e.edgeOrder == OBEdge.SUBCLASS_OF_EDGE &&
          GraphUtil.isClass( (OBNode) e.from)) {
        children.add(e.from);
      }
    }
    return children;
  }

  static private void getAllSubHelper(OBObjectNode node, Set subClasses) {
    Set currentSub = getSubClasses(node);
    for (Iterator i = currentSub.iterator(); i.hasNext(); ) {
      OBObjectNode n = (OBObjectNode) i.next();
      if (!subClasses.contains(n)) {
        subClasses.add(n);
        getAllSubHelper(n, subClasses);
      }
    }
  }

  static protected Set getAllSubClasses(OBObjectNode node) {
    Set subClasses = new HashSet();
    getAllSubHelper(node, subClasses);
    return subClasses;
  }

  static private void getAllSuperHelper(OBObjectNode node, Vector superClasses) {
    Vector currentSuper = GraphUtil.getSuperClasses(node);
    for (Iterator i = currentSuper.iterator(); i.hasNext(); ) {
      OBObjectNode n = (OBObjectNode) i.next();
      if (!superClasses.contains(n)) {
        superClasses.add(n);
        getAllSuperHelper(n, superClasses);
      }
    }
  }

  static protected Vector getAllSuperClasses(OBObjectNode node) {
    Vector superClasses = new Vector();
    getAllSuperHelper(node, superClasses);
    return superClasses;
  }
  /**
   * This is the default constructor
   */
  public EditorGUI() {
    super();
    //initialize();
  }
  public EditorGUI( WordNetDictionary w ) {
    super();
    wordNet = w;
  }
  /**
   * This method initializes tgPanel
   *
   * @return com.touchgraph.graphlayout.TGPanel
   */
  public com.touchgraph.graphlayout.TGPanel getTgPanel() {
   if (tgPanel == null) {
      tgPanel = new com.touchgraph.graphlayout.TGPanel();
      tgPanel.setLayout(new java.awt.BorderLayout());
      tgPanel.setNewGraphEltSet(1);
      tgPanel.setGraphEltSet(0);
      tgPanel.setPreferredSize(new java.awt.Dimension(500, 500));
      tgPanel.setBorder(
          javax.swing.BorderFactory.createLineBorder(
          java.awt.Color.gray,
          1));
    }
    return tgPanel;
  }

  public void setControlVisibility(boolean vis) {
    hScrollBar.setVisible(vis);
    vScrollBar.setVisible(vis);
  }
  /*
   * (non-Javadoc)
   *
   * @see abdn.graph.ontobrowser.GUIPolicy#setOBPointer(abdn.graph.ontobrowser.ESDOntoBrowser)
   */
  public void setOBPointer(ESDOntoBrowser ob) {
    esdOntoBrowser = ob;
    setVerticalSB(ob.hvScroll.getVerticalSB());
    setHorizontalSB(ob.hvScroll.getHorizontalSB());
    tgPanel.add(getHScrollBar(), java.awt.BorderLayout.SOUTH);
    tgPanel.add(getVScrollBar(), java.awt.BorderLayout.EAST);
  }
  /**
   * This method initializes jToolBar
   *
   * @return javax.swing.JToolBar
   */
  public javax.swing.JToolBar getJToolBar() {
    if (jToolBar == null) {
      jToolBar = new javax.swing.JToolBar();
      jToolBar.add(getZoomInButton());
      jToolBar.add(getZoomOutButton());
      jToolBar.setPreferredSize(new java.awt.Dimension(10, 30));
      jToolBar.setBorderPainted(false);
      jToolBar.setFloatable(false);
    }
    return jToolBar;
  }

  //public void loadEntities(EntityVector entities, EntityRelationVector entitiesRelation, boolean optimization) {
  public void loadEntities(EntityVector e, EntityRelationVector er1, EntityRelationVector er2, boolean o) {
    loading = true;
    tgPanel.setGraphEltSet(1);
    tgPanel.clearSelect();
    tgPanel.clearAll();
    tgPanel.setGraphEltSet(0);
    tgPanel.clearSelect();
    tgPanel.clearAll();
    try {
      esdOntoBrowser.krPolicy.read(e, er1, er2, o, esdOntoBrowser.new RestoreExactGraph());
      esdOntoBrowser.ntPolicy.resetOntology(false);
      if (mdataPanel != null) {
        mdataPanel.setMetadata(esdOntoBrowser.krPolicy);
      }
    }
    catch (Exception ex) {
      //      ((OntoFrame)esdOntoBrowser.rtPolicy).setOntoTitle(null);
      showErrorMessage("Error loading OWL file");
      ex.printStackTrace();
    }
    loading = false;
    mode = 0;
    suppressSelectedEvent = true;
    updateObjectList(LIST_CLASSES);
    updateObjectPropertyList();
    updateDataPropertyList();
    suppressSelectedEvent = false;
    if (tgPanel.getCompleteEltSet().nodeNum() > 50) {
      GraphUtil.hideThingNode(false);
      tgPanel.getLocalityUtils().removeMarkedNodes();
      esdOntoBrowser.setSelect(GraphUtil.getThingNode());
      esdOntoBrowser.localityScroll.setRadius(2);
    }
    else {
      tgPanel.setSelect(null);
      GraphUtil.hideThingNode(true);
      tgPanel.getLocalityUtils().removeMarkedNodes();
      esdOntoBrowser.localityScroll.setRadius(6);
    }
    tgPanel.fireResetEvent();
    tgPanel.resetDamper();

  }

  /**
   * This method initializes viewAllButton
   *
   * @return javax.swing.JButton
   */
  private javax.swing.JButton getViewAllButton() {
    if (viewAllButton == null) {
      viewAllButton = new javax.swing.JButton();
      viewAllButton.setText("");
      viewAllButton.setIcon( new javax.swing.ImageIcon(
          getClass().getResource("/abdn/graph/growl/images/All.png")));
      viewAllButton.setToolTipText("View All Ontology");
      viewAllButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent e) {
          if (rBoxIsActive) {
            setRBoxMode(false);
          }
          else {
            esdOntoBrowser.viewAll();
          }
          if (mode == ABOX || mode == RBOX) {
            updateObjectList(LIST_CLASSES);
          }
          mode = ALL;
        }
      });
    }
    return viewAllButton;
  }
  /**
   * This method initializes viewHierarchyButton
   *
   * @return javax.swing.JButton
   */
  private javax.swing.JButton getViewHierarchyButton() {
    if (viewHierarchyButton == null) {
      viewHierarchyButton = new javax.swing.JButton();
      viewHierarchyButton.setText("");
      viewHierarchyButton.setToolTipText("View Class Hierarchy");
      viewHierarchyButton.setIcon(
          new javax.swing.ImageIcon(
          getClass().getResource(
          "/abdn/graph/growl/images/hierarchy.png")));
      viewHierarchyButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent e) {
          setRBoxMode(false);
          viewClassHierarchy();
          if (mode == ABOX || mode == RBOX) {
            updateObjectList(LIST_CLASSES);
          }
          mode = HIERARCH;
        }
      });
    }
    return viewHierarchyButton;
  }

  public JPopupMenu getNodePopup(OBNode node) {
    if (editMode == true) {
      if (nodeEditPopup == null) {
        nodeEditPopup = new JPopupMenu();
      }
      return nodeEditPopup;
    }
    popupNode = node;
    if (nodePopup == null) {
      nodePopup = new JPopupMenu();
      JMenuItem menuItem;

      JMenuItem expandItem = new JMenuItem("Expand node");
      expandItem.addActionListener(expandNodeAction);
      nodePopup.add(expandItem);

      JMenuItem hideItem = new JMenuItem("Hide node");
      hideItem.addActionListener(hideNodeAction);
      nodePopup.add(hideItem);

      JMenuItem localItem = new JMenuItem("Local view");
      localItem.addActionListener(localNodeAction);
      nodePopup.add(localItem);

      nodePopup.addSeparator();

      menuItem = new JMenuItem("Show More");
      menuItem.addActionListener(esdOntoBrowser.showMoreAction);
      nodePopup.add(menuItem);

      menuItem = new JMenuItem("Show Less");
      menuItem.addActionListener(esdOntoBrowser.showLessAction);
      nodePopup.add(menuItem);

      menuItem = new JMenuItem("Toggle Controls");
      menuItem.addActionListener(esdOntoBrowser.toggleControlsAction);
      nodePopup.add(menuItem);

      nodePopup.addPopupMenuListener(new PopupMenuListener() {
        public void popupMenuCanceled(PopupMenuEvent e) {
        }

        public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
          tgPanel.setMaintainMouseOver(false);
          tgPanel.setMouseOverN(null);
          tgPanel.repaintAfterMove();
//          obNodeHintUI.activate();
        }

        public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
        }
      });
    }
    return nodePopup;
  }

  public JPopupMenu getBackPopup() {
    if (editMode == true) {
      if (backEditPopup == null) {
        backEditPopup = new JPopupMenu();
      }
      return backEditPopup;
    }
    if (backPopup == null) {
      backPopup = new JPopupMenu();
    }
    return backPopup;
  }

  /*
   * private ActionListener selectAListener = new ActionListener() { public
   * void actionPerformed(java.awt.event.ActionEvent e) { String label =
   * ((JMenuItem) e.getSource()).getText(); esdOntoBrowser.setLocaleByName(
   * label, esdOntoBrowser.localityScroll.getRadius());
   *  } };
   */

  public JPopupMenu getObPopup() {
    if (obPopup == null) {
      JMenuItem menuItem;
      obPopup = new javax.swing.JPopupMenu();
      menuItem = new JMenuItem("Show More");
      menuItem.addActionListener(esdOntoBrowser.showMoreAction);
      obPopup.add(menuItem);

      menuItem = new JMenuItem("Show Less");
      menuItem.addActionListener(esdOntoBrowser.showLessAction);
      obPopup.add(menuItem);

      menuItem = new JMenuItem("Toggle Controls");
      menuItem.addActionListener(esdOntoBrowser.toggleControlsAction);
      obPopup.add(menuItem);
      obPopup.addPopupMenuListener(new PopupMenuListener() {
        public void popupMenuCanceled(PopupMenuEvent e) {
        }

        public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
          tgPanel.setMaintainMouseOver(false);
          tgPanel.setMouseOverN(null);
          tgPanel.repaintAfterMove();
          //          obNodeHintUI.activate();
        }

        public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
        }
      });

    }
    return obPopup;

  }

  /*
   * (non-Javadoc)
   *
       * @see abdn.graph.ontobrowser.GUIPolicy#showOBPopup(java.awt.event.MouseEvent)
   */
  public void showOBPopup(MouseEvent e) {
    getObPopup().show(e.getComponent(), e.getX(), e.getY());
    //System.out.println("MouseEvent: " + e.toString() );
  }

  private SortedSet getObjects(int obj_type) {
    SortedSet objects = new TreeSet();
    Set owlObjects;
    if (obj_type == LIST_CLASSES) {
      owlObjects = esdOntoBrowser.getObjectNodes(RenderGrowl.OBJ_CLASS);
    }
    else if (obj_type == LIST_INSTANCES) {
      owlObjects =
          esdOntoBrowser.getObjectNodes(RenderGrowl.OBJ_INDIVIDUAL);
    }
    else {
      owlObjects =
          esdOntoBrowser.getRelationNodes(
          RenderGrowl.REL_OBJECT_PROPERTY);
      owlObjects.addAll(
          esdOntoBrowser.getRelationNodes(RenderGrowl.REL_DATA_PROPERTY));
    }

    for (Iterator it = owlObjects.iterator(); it.hasNext(); ) {
      OBNode obj = (OBNode) it.next();
      objects.add(obj.getLabel());
    }

    return objects;
  }

  /**
   * This method initializes classSelectCombo
   *
   * @return IncrMatchComboBox
   */
  private IncrMatchComboBox getSelectCombo() {
    if (classSelectCombo == null) {
      classSelectCombo = new IncrMatchComboBox(null, false);
      classSelectCombo.setName("Select");
      classSelectCombo.setAlignmentX(20.0F);
      classSelectCombo.setAlignmentY(0.5F);
      classSelectCombo.setAutoscrolls(true);
      classSelectCombo.setMaximumSize(new java.awt.Dimension(200, 18));
      classSelectCombo.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent e) {
          if (!suppressSelectedEvent && !inNotification) {
            IncrMatchComboBox cb =
                (IncrMatchComboBox) e.getSource();
            if (cb.gotMatch()) {
              String label = (String) cb.getSelectedItem();
              OBNode node = esdOntoBrowser.getNodeByName(label);
              if (node != null) {
                tgPanel.setLocale(node, esdOntoBrowser.localityScroll.getRadius());
                esdOntoBrowser.setSelect(node);
                if (!tgPanel.getVisibleLocality().contains(node)) {
                  tgPanel.layoutOnce();
                }
                tgPanel.fireResetEvent();
                esdOntoBrowser.hvScroll.slowScrollToCenter(node);
              }
            }
          }
        }
      });
    }
    return classSelectCombo;
  }

  private void updateObjectList(int obj_type) {
    boolean saveLayout = doLayout;
    doLayout = false;
    if (obj_type != LIST_RELATIONS) {
      if (lastSplit >= 0) {
        splitPane.setDividerLocation(lastSplit);
        lastSplit = -1;
      }
      updateClassTree();
    }
    else {
      getSelectPanel().setClasses(null);
      lastSplit = splitPane.getDividerLocation();
      splitPane.setDividerLocation(0);
    }
    IncrMatchComboBox cb = getSelectCombo();
    String sel = (String) cb.getSelectedItem();
    boolean gotSel = false;
    Vector strings = new Vector();
    SortedSet objs = getObjects(obj_type);
    for (Iterator iter = objs.iterator(); iter.hasNext(); ) {
      String s = (String) iter.next();
      strings.add(s);
      if (!gotSel && s.equals(sel)) {
        gotSel = true;
      }
    }
    cb.replaceAll(strings, true);
    cb.setSelectedItem(gotSel ? sel : "");
    doLayout = saveLayout;
  }
  private ClassViewer getSelectPanel() {
    if (selectPanel == null) {
      selectPanel = new ClassViewer(this);
    }
    return selectPanel;
  }

  private void updateClassTree() {
    getSelectPanel().setClasses(esdOntoBrowser.getObjectNodes(RenderGrowl.OBJ_CLASS));
  }

  private void updateDataPropertyList() {
    SortedSet objects = new TreeSet();
    Set owlObjects;
    owlObjects =
        esdOntoBrowser.getRelationNodes(RenderGrowl.REL_DATA_PROPERTY);
    for (Iterator it = owlObjects.iterator(); it.hasNext(); ) {
      OBNode obj = (OBNode) it.next();
      objects.add(obj.getName()); //wasLabel
    }
    Vector strings = new Vector();
    for (Iterator iter = objects.iterator(); iter.hasNext(); ) {
      strings.add(iter.next());
    }
    //relPanel.updateDataPropertyList(strings);
  }

  private void updateObjectPropertyList() {
    SortedSet objects = new TreeSet();
    Set owlObjects;
    owlObjects =
        esdOntoBrowser.getRelationNodes(RenderGrowl.REL_OBJECT_PROPERTY);
    for (Iterator it = owlObjects.iterator(); it.hasNext(); ) {
      OBNode obj = (OBNode) it.next();
      objects.add(obj.getName()); //wasLabel
    }
    Vector strings = new Vector();
    for (Iterator iter = objects.iterator(); iter.hasNext(); ) {
      strings.add(iter.next());
    }
    //relPanel.updateObjectPropertyList(strings);
  }

  /**
   * This method initializes viewRBoxButton
   *
   * @return javax.swing.JButton
   */
  private javax.swing.JButton getToggleRBoxButton() {
    if (toggleRBoxButton == null) {
      toggleRBoxButton = new javax.swing.JButton();
      toggleRBoxButton.setText("");
      toggleRBoxButton.setIcon(new javax.swing.ImageIcon(
          getClass().getResource( "/abdn/graph/growl/images/Rbox.png")));
      toggleRBoxButton.setToolTipText("Toggle RBox View");
      toggleRBoxButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent e) {
          setRBoxMode(!rBoxIsActive);
        }
      });
    }
    return toggleRBoxButton;
  }

  private void setRBoxMode(boolean shouldBeActive) {
    suppressSelectedEvent = true;
    if (!rBoxIsActive && shouldBeActive) {
      esdOntoBrowser.clearSelect();
      tgPanel.setGraphEltSet(1);
      updateObjectList(LIST_RELATIONS);
      esdOntoBrowser.viewAll();
      toggleRBoxButton.setSelected(true);
      this.updateUI();

    }
    else if (rBoxIsActive && !shouldBeActive) {
      esdOntoBrowser.clearSelect();
      tgPanel.setGraphEltSet(0);
      updateObjectList(LIST_CLASSES);
      esdOntoBrowser.viewAll();
      toggleRBoxButton.setSelected(false);
      this.updateUI();

    }
    suppressSelectedEvent = false;
    rBoxIsActive = shouldBeActive;

    if (rBoxIsActive) {
      mode = RBOX;
    }
    LocalityUtils localityUtils = tgPanel.getLocalityUtils();
    localityUtils.removeMarkedNodes();
    tgPanel.fireResetEvent();
    tgPanel.resetDamper();
  }

  /**
   * This method initializes toggleEditButton
   *
   * @return javax.swing.JButton
   */
  private javax.swing.JButton getToggleEditButton() {
    if (toggleEditButton == null) {
      toggleEditButton = new javax.swing.JButton();
      toggleEditButton.setToolTipText("Switch to Edit Mode");
      toggleEditButton.setIcon(editIco);

      toggleEditButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent e) {
          setEditMode(!editMode);
        }

      });
    }
    return toggleEditButton;
  }

  private void setEditMode(boolean edit) {
    if (editMode != edit) {
      editMode = edit;
      if (!editMode) {
        esdOntoBrowser.tgUIManager.activate("Navigate");
        getToggleEditButton().setIcon(editIco);
        getToggleEditButton().setToolTipText("Switch to Edit Mode");
      }
      else {
        esdOntoBrowser.tgUIManager.activate("Edit");
        getToggleEditButton().setIcon(browseIco);
        getToggleEditButton().setToolTipText("Switch to Browse Mode");
      }
    }
    if (!editMode) {
      setLayoutMode(true);
    }
    else {
      setLayoutMode(false);
    }
  }

  /**
   * This method initializes jLabel
   *
   * @return javax.swing.JLabel
   */
  private javax.swing.JLabel getJLabel() {
    if (jLabel == null) {
      jLabel = new javax.swing.JLabel();
      jLabel.setText("        ");
    }
    return jLabel;
  }

  public void setPopupNode(Node n) {
    popupNode = n;
  }

  /*
   * (non-Javadoc)
   *
   * @see abdn.graph.ontobrowser.GUIPolicy#processLBClick(int,
   *      int)
   */
  public void processLBClick(int x, int y) {
    if (ESDOntoBrowser.deleteOnLClick || editToolPressed == 0 ||
        createNodeType == 0) {
      ESDOntoBrowser.deleteOnLClick = false;
      return;
    }

    try {
      if (editingItem == EDITING_OBJECT) {
        suppressSelectedEvent = true;
        OBObjectNode on = esdOntoBrowser.createNewObject(x, y, createNodeType);
        if (createNodeType == RenderGrowl.OBJ_CLASS) {
          addThingSuperClass(on);
        }
        esdOntoBrowser.setSelect(on);
        //tgPanel.repaint();
        esdOntoBrowser.getTGPanel().setMouseOverN(on);
        suppressSelectedEvent = false;
      }
    } catch (TGException e) {
      e.printStackTrace();
    }

  }

  private void addThingSuperClass(OBObjectNode on) {
    OBObjectNode thing = GraphUtil.getThingNode();
    OBEdge e = esdOntoBrowser.createNewEdge(on, thing, OBEdge.SUBCLASS_OF_EDGE);
    if (thing.isHiden) {
      tgPanel.hideEdge(e);
    }
  }

  public static boolean isSet(OBNode obj) {
    if ( (obj instanceof OBObjectNode)
        && ( ( (OBObjectNode) obj).objectCategory == RenderGrowl.OBJ_INTERSECTION
            || ( (OBObjectNode) obj).objectCategory == RenderGrowl.OBJ_UNION
            || ( (OBObjectNode) obj).objectCategory == RenderGrowl.OBJ_COMPLEMENT)) {
      return true;
    }
    else {
      return false;
    }

  }

  public static boolean isClassBase(OBNode node) {
    if ( (node instanceof OBObjectNode)) {
      switch ( ( (OBObjectNode) node).objectCategory) {
        case RenderGrowl.OBJ_CLASS:
        case RenderGrowl.OBJ_INTERSECTION:
        case RenderGrowl.OBJ_UNION:
        case RenderGrowl.OBJ_COMPLEMENT:
          return true;
        default:
          return false;
      }
    }

    return false;
  }

  public static boolean isObjectType(OBNode obj, int type) {
    if ( (obj instanceof OBObjectNode)
        && ( (OBObjectNode) obj).objectCategory == type) {
      return true;
    }
    else {
      return false;
    }
  }

  public static boolean isUntypedEnumeration(OBNode node) {
    if (isObjectType(node, RenderGrowl.OBJ_ONE_OF)
        && !node.hasInput()
        && !node.hasOutput()) {
      return true;
    }
    else {
      return false;
    }
  }

  private int owlDLCompliance(OBObjectNode obj1) {
    if (editingItem == EDITING_OBJECT) {
      switch (createNodeType) {
        case RenderGrowl.OBJ_CLASS:
        case RenderGrowl.OBJ_INDIVIDUAL:
        case RenderGrowl.OBJ_DATATYPE:
        case RenderGrowl.OBJ_DATAVALUE:
        case RenderGrowl.OBJ_INTERSECTION:
        case RenderGrowl.OBJ_UNION:
        case RenderGrowl.OBJ_COMPLEMENT:
      }
    }
    else {
      switch (editToolPressed) {
        case RenderGrowl.REL_OBJECT_PROPERTY_VALUE:
        case RenderGrowl.REL_DATA_PROPERTY_VALUE:
        case RenderGrowl.REL_OBJECT_PROPERTY:
        case RenderGrowl.REL_DATA_PROPERTY:
        case RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_SOME:
        case RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_ALL:
        case RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_VALUE:
        case RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_CARD:

        case RenderGrowl.REL_DATA_PROPERTY_RESTRICT_SOME:
        case RenderGrowl.REL_DATA_PROPERTY_RESTRICT_ALL:
        case RenderGrowl.REL_DATA_PROPERTY_RESTRICT_VALUE:
        case RenderGrowl.REL_DATA_PROPERTY_RESTRICT_CARD:

        case RenderGrowl.REL_ARE_EQUIVALENT:
        case RenderGrowl.REL_ARE_SAME:
        case RenderGrowl.REL_ARE_DIFFERENT:
        case RenderGrowl.REL_ARE_DISJOINT:

      }

    }
    return 0;
  }
  /**
   * Method addLink
   * Add link(s) and node(s) after dragging from a node into empty space
   * @param fromObj
   */
  public void addLink(OBNode fromObj, int x, int y) {
    if (fromObj instanceof OBObjectNode) {
      switch ( ( (OBObjectNode) fromObj).objectCategory) {
        case RenderGrowl.OBJ_CLASS: // drag from class node
          dragFromClassNode( (OBObjectNode) fromObj, x, y);
          break;
        case RenderGrowl.OBJ_INDIVIDUAL:
          dragFromIndividualNode( (OBObjectNode) fromObj, x, y);
          break;
      }
    }
    tgPanel.repaint();
  }

  private void dragFromClassNode(OBObjectNode fromObj, int x, int y) {
    OBObjectNode newNode;
    if (editingItem == EDITING_OBJECT) {
     /* switch (createNodeType) {
        case RenderGrowl.OBJ_CLASS:
           case RenderGrowl.REL_SUBCLASS:
          newNode = createSubclass(fromObj, x, y);
          if (newNode != null) {
            updateClassTree();
            esdOntoBrowser.setSelect(newNode);
          }
          break;
        case RenderGrowl.OBJ_INDIVIDUAL:
          // case RenderGrowl.REL_INSTANCE:
          newNode = createIndividual( (OBObjectNode) fromObj, x, y);
          if (newNode != null) {
            esdOntoBrowser.setSelect(newNode);
          }
          break;
      }*/
    }
    else {
      switch (createNodeType) {
        case RenderGrowl.REL_OBJECT_PROPERTY:
        case RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_ALL:
        case RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_CARD:
        case RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_SOME:
        case RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_VALUE:
        case RenderGrowl.REL_DATA_PROPERTY:
        case RenderGrowl.REL_DATA_PROPERTY_RESTRICT_ALL:
        case RenderGrowl.REL_DATA_PROPERTY_RESTRICT_CARD:
        case RenderGrowl.REL_DATA_PROPERTY_RESTRICT_SOME:
        case RenderGrowl.REL_DATA_PROPERTY_RESTRICT_VALUE:
          //TODO require user input to say which property to create a value or restriction of
          processLBClick(x, y); // creates new node
          OBNode node =
              (OBNode) esdOntoBrowser.getTGPanel().getSelect();
          if (node != null) {
            addLink(fromObj, x, y);
          }
          break;
      }
    }
  }

  private void dragFromIndividualNode(OBObjectNode fromObj, int x, int y) {
    if (editingItem == EDITING_OBJECT) {
    }
    else {
      switch (editToolPressed) {
        case RenderGrowl.REL_DATA_PROPERTY_VALUE:
        case RenderGrowl.REL_OBJECT_PROPERTY_VALUE:
          processLBClick(x, y); // creates new node
          OBNode node =
              (OBNode) esdOntoBrowser.getTGPanel().getSelect();
          if (node != null) {
            addLink(fromObj, x, y);
          }
          break;
      }
    }
  }
  /**
   * @return MetadataPanel
   * @see MetadataPanel
   */
  public MetadataPanel getMDataPanel() {
    if (mdataPanel == null) {
      mdataPanel = new MetadataPanel(esdOntoBrowser.krPolicy, this);
      mdataPanel.setPreferredSize(new java.awt.Dimension(500, 500));
      mdataPanel.setBorder(
          javax.swing.BorderFactory.createLineBorder(
          java.awt.Color.gray,
          1));
    }
    else {
      mdataPanel.setMetadata(esdOntoBrowser.krPolicy);
    }
    return mdataPanel;
  }
  /**
   * This method initializes layoutButton
   *
   * @return javax.swing.JButton
   */
  private javax.swing.JButton getLayoutButton() {
    if (layoutButton == null) {
      layoutButton = new javax.swing.JButton();
      layoutButton.setText("");
      layoutButton.setIcon(stopLayoutIco);
      layoutButton.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent e) {
          setLayoutMode(!doLayout);
        }
      });
    }
    return layoutButton;
  }

  private void setLayoutMode(boolean doLt) {
    doLayout = doLt;
    esdOntoBrowser.getTGPanel().setLayoutMode(doLayout);
    if (doLayout) {
      layoutButton.setIcon(stopLayoutIco);
      layoutButton.setToolTipText("Stop Layout Engine");
    }
    else {
      layoutButton.setIcon(doLayoutIco);
      layoutButton.setToolTipText("Start Layout Engine");
    }
  }




  public void addEdgeNotification(OBEdge e) {
    if (!loading) {
      if (e.edgeOrder == OBEdge.SUBCLASS_OF_EDGE
          && GraphUtil.isClass( (OBNode) e.to)
          && GraphUtil.isClass( (OBNode) e.from)) {
        updateObjectList(LIST_CLASSES);
      }
    }
  }

  public void deleteEdgeNotification(OBEdge e) {
    if (!loading) {
      if (e.edgeOrder == OBEdge.SUBCLASS_OF_EDGE) {
        if (GraphUtil.isClass( (OBNode) e.from) &&
            GraphUtil.getSuperClasses( (OBObjectNode) e.from).size() == 0) { // add Thing superclass grounding if class has no other superclasses
          addThingSuperClass( (OBObjectNode) e.from); // notification generated by this does the updateObjectList()
        }
        else {
          updateObjectList(LIST_CLASSES);
        }
      }
    }
  }


  private Set getDataPropertyRestrictions() {
    Set restrictions =
        esdOntoBrowser.getRelationNodes(
        RenderGrowl.REL_DATA_PROPERTY_RESTRICT_ALL);
    restrictions.addAll(
        esdOntoBrowser.getRelationNodes(
        RenderGrowl.REL_DATA_PROPERTY_RESTRICT_SOME));
    restrictions.addAll(
        esdOntoBrowser.getRelationNodes(
        RenderGrowl.REL_DATA_PROPERTY_RESTRICT_CARD));
    restrictions.addAll(
        esdOntoBrowser.getRelationNodes(
        RenderGrowl.REL_DATA_PROPERTY_RESTRICT_VALUE));
    return restrictions;
  }

  private Set getObjectPropertyRestrictions() {
    Set restrictions;
    restrictions =
        esdOntoBrowser.getRelationNodes(
        RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_ALL);
    restrictions.addAll(
        esdOntoBrowser.getRelationNodes(
        RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_SOME));
    restrictions.addAll(
        esdOntoBrowser.getRelationNodes(
        RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_CARD));
    restrictions.addAll(
        esdOntoBrowser.getRelationNodes(
        RenderGrowl.REL_OBJECT_PROPERTY_RESTRICT_VALUE));
    return restrictions;
  }

  public void showErrorMessage(String msg) {
    JOptionPane.showMessageDialog(
        this,
        msg,
        "GrOWL Error",
        JOptionPane.ERROR_MESSAGE);
  }

  public void restoreAll() {
    boolean hidden = GraphUtil.getThingNode().isHiden;
    tgPanel.clearSelect();
    tgPanel.restoreAll();
    GraphUtil.hideThingNode(hidden);
  }

} //  @jve:visual-info decl-index=0 visual-constraint="7,10"
//  @jve:visual-info decl-index=0 visual-constraint="7,10"
